﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using KxParser.Lexer.Pm;
using KxParser.Parser.LR0;
using System.Text.RegularExpressions;
using System.Xml;
using KxParser.Parser.Lr0;

namespace KxParserTestWin
{
	public partial class MainForm : Form
	{
		public MainForm()
		{
			InitializeComponent();
		}

		private void MainForm_Load(object sender, EventArgs e)
		{
			Logger.ListView = listView1;
			fillTokenDefsGrid(null);
			fillBnfGrammarGrid(null);
		}

		void fillTokenDefsGrid(TokenDefinition[] tokenDefs)
		{
			if(tokenDefs == null)
				tokenDefs = loadTokenDefinitionsExample();

			DataTable dt = new DataTable();
			dt.Columns.Add("Name", typeof(string));
			dt.Columns.Add("Content", typeof(string));
			dt.Columns.Add("IsIgnored", typeof(bool));

			foreach (TokenDefinition td in tokenDefs)
			{
				dt.Rows.Add(td.Token, td.RegularExpression, td.IsIgnored);
			}
			dgvTokens.DataSource = dt;
		}

		TokenDefinition[] loadTokenDefinitionsExample()
		{
			List<TokenDefinition> tokenDefs = new List<TokenDefinition>();

			tokenDefs.Add(new TokenDefinition(@"\d+\,\d+([eE][-+]?\d+)?", "NUM_FLOAT"));
			tokenDefs.Add(new TokenDefinition(@"\d+\.\d+([eE][-+]?\d+)?", "NUM_FLOAT"));
			tokenDefs.Add(new TokenDefinition(@"\d+", "NUM_INT"));
			tokenDefs.Add(new TokenDefinition(@"\+", "+"));
			tokenDefs.Add(new TokenDefinition(@"\-", "-"));
			tokenDefs.Add(new TokenDefinition(@"\*", "*"));
			tokenDefs.Add(new TokenDefinition(@"\/", "/"));
			tokenDefs.Add(new TokenDefinition(@"\(", "("));
			tokenDefs.Add(new TokenDefinition(@"\)", ")"));
			tokenDefs.Add(new TokenDefinition(@"\s|\t|\r|\n|\u000C", "WHITESPACE", true));

			return tokenDefs.ToArray();
		}


		void fillBnfGrammarGrid(BnfGrammar[] bnfGrammars)
		{
			if(bnfGrammars == null)
				bnfGrammars = loadBnfGrammarExample();

			DataTable dt = new DataTable();
			dt.Columns.Add("Name", typeof(string));
			dt.Columns.Add("Rule", typeof(string));

			foreach (BnfGrammar g in bnfGrammars)
			{
				dt.Rows.Add(g.Name, g.Rule);
			}

			dgvBnfGrammar.DataSource = dt;
		}


		BnfGrammar[] loadBnfGrammarExample()
		{
			List<BnfGrammar> bnfGrammars = new List<BnfGrammar>();

			bnfGrammars.Add(new BnfGrammar("S'", "m $"));
			bnfGrammars.Add(new BnfGrammar("m", "m math_op a"));
			bnfGrammars.Add(new BnfGrammar("m", "a"));
			bnfGrammars.Add(new BnfGrammar("a", "num"));
			bnfGrammars.Add(new BnfGrammar("a", "( m )"));

			bnfGrammars.Add(new BnfGrammar("math_op", "+"));
			bnfGrammars.Add(new BnfGrammar("math_op", "-"));
			bnfGrammars.Add(new BnfGrammar("math_op", "*"));
			bnfGrammars.Add(new BnfGrammar("math_op", "/"));
			bnfGrammars.Add(new BnfGrammar("num", "( num )"));
			bnfGrammars.Add(new BnfGrammar("num", "- num1"));
			bnfGrammars.Add(new BnfGrammar("num", "num1"));
			bnfGrammars.Add(new BnfGrammar("num1", "NUM_FLOAT"));
			bnfGrammars.Add(new BnfGrammar("num1", "NUM_INT"));

			return bnfGrammars.ToArray();
		}

		private void btnParse_Click(object sender, EventArgs e)
		{
			if (cbxParser.SelectedItem == "LR(0) parser")
			{
				doLrZeroParsing();
			}
		}


		void doLrZeroParsing()
		{
			// Lexical analyze
			TextReader r = new StringReader(tbInput.Text);
			List<TokenDefinition> tokenDefinitions = new List<TokenDefinition>();
			DataTable dt = (DataTable)dgvTokens.DataSource;
			bool isIgnored;
			foreach (DataRow row in dt.Rows)
			{
				isIgnored = row[2] != null && Convert.ToBoolean(row[2]);
				tokenDefinitions.Add(new TokenDefinition(row[1].ToString(), row[0], isIgnored));
			}

			PmLexer pmLexer = new PmLexer(r, tokenDefinitions.ToArray(), new[] { " ", "(", ")" });

			if (pmLexer.GetState() != PmLexer.ELexerState.OK)
			{
				// Respond to error. To get error specifications, call GetErrorTokens() method and process all error tokens
				foreach (var errorToken in pmLexer.GetErrorTokens())
				{
					Logger.New("Lexical", string.Format("Unknown token '{0}'!", errorToken.Content), string.Format("{0}-{1}", errorToken.StartIndex, errorToken.EndIndex));
					tbInput.BackColor = Color.Red;
				}
			}

			else
			{
				// Syntax
				var tokens = pmLexer.GetNonIgnoredTokens();
				var terminals = (from t in tokens select t.Name).Distinct().ToArray();

				List<BnfGrammar> bnfGrammars = new List<BnfGrammar>();
				DataTable dtGrammars = (DataTable)dgvBnfGrammar.DataSource;
				foreach (DataRow row in dtGrammars.Rows)
				{
					bnfGrammars.Add(new BnfGrammar(row[0].ToString(), row[1].ToString()));
				}

				var lr0Parser = new Lr0Parser(bnfGrammars.ToArray(), terminals);

				bool parserResult = lr0Parser.Parse(tokens);
				if (!parserResult)
				{
					tbInput.BackColor = Color.Red;

					var errorToken = lr0Parser.GetErrorToken();
					if (errorToken != null)
					{
						if (!errorToken.Content.Equals("$"))
							Logger.New("Syntax", string.Format("Illegal character '{0}'", errorToken.Content), string.Format("{0}-{1}", errorToken.StartIndex, errorToken.EndIndex));
						else
							Logger.New("Syntax", "Illegal end of input string", "");
					}
				}
				else
				{
					tbInput.BackColor = Color.LightGreen;
					Logger.New("OK", "Parse OK", "");
				}
			}
		}



		private void tbInput_TextChanged(object sender, EventArgs e)
		{
			tbInput.BackColor = Color.White;
		}

		private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
		{
			//saveTokenDefinitionsToFile();
			//saveBnfGrammarsToFile();
		}

		private void btnLoadTokenDefinitions_Click(object sender, EventArgs e)
		{
			OpenFileDialog openFileDialog = new OpenFileDialog();
			openFileDialog.DefaultExt = "*.xml";
			openFileDialog.Filter = "XML Files|*.xml";
			openFileDialog.Title = "Load token definitions from file";
			openFileDialog.RestoreDirectory = true;

			if (openFileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
			{
				DataSet ds = new DataSet();
				ds.ReadXml(openFileDialog.FileName);
				DataView dv = new DataView(ds.Tables[0]);
				dgvTokens.DataSource = dv;
			}
		}

		private void btnSaveTokenDefinitions_Click(object sender, EventArgs e)
		{
			SaveFileDialog saveFileDialog = new SaveFileDialog();
			saveFileDialog.DefaultExt = "*.xml";
			saveFileDialog.Filter = "XML Files|*.xml";
			saveFileDialog.Title = "Save token definitions to file";
			saveFileDialog.RestoreDirectory = true;

			if (saveFileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
			{
				DataTable dt = new DataTable("tokedefstable");
				dt.Columns.Add("Name", typeof(string));
				dt.Columns.Add("Content", typeof(string));
				dt.Columns.Add("IsIgnored", typeof(bool));

				DataRow myrow;
				int icols = dgvTokens.Columns.Count;
				foreach (DataGridViewRow drow in this.dgvTokens.Rows)
				{
					myrow = dt.NewRow();
					for (int i = 0; i <= icols - 1; i++)
					{
						if (i == 2 && drow.Cells[i].Value == null)
						{
							drow.Cells[i].Value = false;
						}

						myrow[i] = drow.Cells[i].Value;
					}
					dt.Rows.Add(myrow);
				}

				dt.WriteXml(saveFileDialog.FileName);
			}
		}

		private void btnLoadBnfGrammars_Click(object sender, EventArgs e)
		{
			OpenFileDialog openFileDialog = new OpenFileDialog();
			openFileDialog.DefaultExt = "*.xml";
			openFileDialog.Filter = "XML Files|*.xml";
			openFileDialog.Title = "Load Backus-Naur grammars from file";
			openFileDialog.RestoreDirectory = true;

			if (openFileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
			{
				DataSet ds = new DataSet();
				ds.ReadXml(openFileDialog.FileName);
				DataView dv = new DataView(ds.Tables[0]);
				dgvBnfGrammar.DataSource = dv;
			}
		}

		private void btnSaveBnfGrammars_Click(object sender, EventArgs e)
		{
			SaveFileDialog saveFileDialog = new SaveFileDialog();
			saveFileDialog.DefaultExt = "*.xml";
			saveFileDialog.Filter = "XML Files|*.xml";
			saveFileDialog.Title = "Save Backus-Naur grammars to file";
			saveFileDialog.RestoreDirectory = true;

			if (saveFileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
			{
				DataTable dt = new DataTable("grammarstable");
				dt.Columns.Add("Name", typeof(string));
				dt.Columns.Add("Rule", typeof(string));

				DataRow myrow;
				int icols = dgvBnfGrammar.Columns.Count;
				foreach (DataGridViewRow drow in this.dgvBnfGrammar.Rows)
				{
					myrow = dt.NewRow();
					for (int i = 0; i <= icols - 1; i++)
					{
						if (i == 2 && drow.Cells[i].Value == null)
						{
							drow.Cells[i].Value = false;
						}

						myrow[i] = drow.Cells[i].Value;
					}
					dt.Rows.Add(myrow);
				}

				dt.WriteXml(saveFileDialog.FileName);
			}
		}

		void moveRowUp(DataGridView dgv)
		{
			object o;

			if (dgv.CurrentRow.Index > 0 && !dgv.CurrentRow.IsNewRow)
			{
				for (int i = 0; i < dgv.ColumnCount; i++)
				{
					o = dgv.Rows[dgv.CurrentRow.Index - 1].Cells[i].Value;
					dgv.Rows[dgv.CurrentRow.Index - 1].Cells[i].Value = dgv.Rows[dgv.CurrentRow.Index].Cells[i].Value;
					dgv.Rows[dgv.CurrentRow.Index].Cells[i].Value = o;
				}
				dgv.CurrentCell = dgv.Rows[dgv.CurrentRow.Index - 1].Cells[0];
			}
		}

		void moveRowDown(DataGridView dgv)
		{
			object o;

			if (dgv.CurrentRow.Index < dgv.RowCount - 2)
			{
				for (int i = 0; i < dgv.ColumnCount; i++)
				{
					o = dgv.Rows[dgv.CurrentRow.Index + 1].Cells[i].Value;
					dgv.Rows[dgv.CurrentRow.Index + 1].Cells[i].Value = dgv.Rows[dgv.CurrentRow.Index].Cells[i].Value;
					dgv.Rows[dgv.CurrentRow.Index].Cells[i].Value = o;
				}
				dgv.CurrentCell = dgv.Rows[dgv.CurrentRow.Index + 1].Cells[0];
			}
		}

		private void btnTokenDefinitionUp_Click(object sender, EventArgs e)
		{
			moveRowUp(dgvTokens);
		}

		private void btnTokenDefinitionDown_Click(object sender, EventArgs e)
		{
			moveRowDown(dgvTokens);
		}

		private void btnBnfGrammarUp_Click(object sender, EventArgs e)
		{
			moveRowUp(dgvBnfGrammar);
		}

		private void btnBnfGrammarDown_Click(object sender, EventArgs e)
		{
			moveRowDown(dgvBnfGrammar);
		}

		private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
		{
			btnParse.Enabled = cbxParser.SelectedIndex >= 0;
		}

		private void btnDeleteBnfGrammar_Click(object sender, EventArgs e)
		{
			if(!dgvBnfGrammar.CurrentRow.IsNewRow)
				dgvBnfGrammar.Rows.Remove(dgvBnfGrammar.CurrentRow);
		}

		private void btnDeleteTokenDefinition_Click(object sender, EventArgs e)
		{
			if(!dgvTokens.CurrentRow.IsNewRow)
				dgvTokens.Rows.Remove(dgvTokens.CurrentRow);
		}
	}
}
